#include "indexOperate.h"

FILE_MAP filesGroup;
CREATE_FILE_THREAD file_thread(5, (void*)&createFileThread);

FILE_ID_STRUCT* findFile(STRING group) {
	lprint.printDebug("function: findFile \n");
	if (filesGroup.count(group) == 0) {
		printf("not find group: %s; \n", group.c_str());
		return NULL;
	}
	return &filesGroup[group];
}

int fileCount(const char *group_name) {
	lprint.printDebug("function: fileCount \n");
	STRING group(group_name);
	FILE_ID_STRUCT *file_struct = findFile(group);
	if (NULL == file_struct)
		return 0;
	FILE_NUM_MAP num_map = file_struct->fileIds;
	return num_map.size();
}

int calcFileId(STRING group, FILE_LEN_TYPE pos) {
	lprint.printDebug("function: calcFileId \n");
	FILE_ID_STRUCT *file_struct = findFile(group);
	FILE_LEN_TYPE size = file_struct->size;
	int file_id = FILE_NUM(pos, size);
	if (file_struct->fileIds.count(file_id) > 0) {
		return file_struct->fileIds[file_id];
	}

	printf("not find file group name: %s, pos: %llu, id: %d \n", group.c_str(), pos, file_id);
	return ERR_NUM;
}

MEM_FILE_ITEM readMemoryMap(STRING group_name, FILE_LEN_TYPE pos) {
	lprint.printDebug("function: readMemoryMap \n");
	int id = calcFileId(group_name, pos);
	MEM_FILE_ITEM mem = findMemoryFile(id);
	return mem;
}

int createGroupFile(STRING group, FILE_LEN_TYPE pos) {
	lprint.printDebug("function: createGroupFile \n");
	FILE_ID_STRUCT *file_struct = findFile(group);
	FILE_LEN_TYPE size = file_struct->size;
	int file_id = FILE_NUM(pos, size);
	if (innerCreateGroup(group, file_struct, file_id)){
		addCreateFile(group, file_id);
	}
	return file_struct->fileIds[file_id];
}

bool innerCreateGroup(STRING group, FILE_ID_STRUCT *file_struct, int file_id){
	lprint.printDebug("function: innerCreateGroup \n");
	if (file_struct->fileIds.count(file_id) == 0) {
		STRING file_name = FULL_PATH(file_struct->path,
				TO_STRING(file_struct->fileIds.size())+INDEX_EXT);
		createBindFileValue(group, file_name.c_str(), file_struct->size,
				file_id);
		return true;
	}
	return false;
}

void addCreateFile(STRING group_name, int file_id){
	lprint.printDebug("function: addCreateFile \n");
	CREATE_FILE cf;
	cf.file_id = file_id;
	cf.group = group_name;
	file_thread.add(cf);
}

void createFileThread(CREATE_FILE* cf){
	lprint.printDebug("function: createFileThread \n");
	STRING group_name = cf->group;
	FILE_LEN_TYPE pos = 0;
	int create_count = 1;
	while (EMPTY != group_name && create_count-- > 0){
		FILE_ID_STRUCT *file_struct = findFile(group_name);
		if (NULL != file_struct){
			int file_size = file_struct->fileIds.size() - 1;
			if (cf->file_id >= file_size){
				pos = file_struct->fileIds.size() * file_struct->size + 1;
				int file_id = FILE_NUM(pos, file_struct->size);
				innerCreateGroup(group_name, file_struct, file_id);
			} else {
				break;
			}
		}
	}
}

void loadGroupFile(const char* group_name, const char* file_name){
	lprint.printDebug("function: loadGroupFile file name: %s \n", file_name);
	if (access(file_name, F_OK) >= 0){
		int file_id=0, file_num=0;
		STRING group(group_name);
		loadMemory(file_name, file_id, file_num);
		FILE_ID_STRUCT *file_struct = &filesGroup[group];
		file_struct->fileIds[file_num] = file_id;
		lprint.printFile("load group file: %s; file id: %llu; file num: %llu \n", file_name, file_id, file_num);
	}
}

FileInfoStruct createBindFileValue(STRING group, const char *file_name,
FILE_LEN_TYPE size, int file_num) {
	lprint.printDebug("function: createBindFileValue \n");
	int id = createMemory(file_name, size);
	FILE_ID_STRUCT *file_struct = &filesGroup[group];
	file_struct->fileIds[file_num] = id;
	FileInfoStruct file_inf;
	file_inf.id = id;
	file_inf.num = file_num;
	setFileInfo(id, file_num, size);
	return file_inf;
}

int initData(const char *group_name, const char *path, FILE_LEN_TYPE size, FILE_LEN_TYPE index_len) {
	lprint.printDebug("function: initData \n");
	STRING group(group_name);
	int id = 0, rtn=0;
	if (filesGroup.count(group) == 0) {
		STRING str_path(path);
		STRING filename = FULL_PATH(str_path, INDEX_SETTING);
		if (access(filename.c_str(), F_OK) < 0) {
			id = createMemory(filename.c_str(), GROUP_HEAD_LEN);
			GROUP_HEAD_STRUCT group_item;
			group_item.position = 0;
			group_item.shift = 0;
			group_item.size = size;
			group_item.tag.count = 0;
			group_item.tag.last = 0;
			group_item.tag.max = 0;
			group_item.tag.min = 0;
			group_item.tag.top = 0;
			FILE_ID_STRUCT file;
			file.size = size;
			file.head = id;
			file.path = str_path;
			file.top = 0;
			file.max = 0;
			file.min = 0;
			filesGroup[group] = file;
			writeMemory(id, (char*) &group_item, 0, GROUP_HEAD_LEN);
			char file_data[index_len];
			memset((char*) &file_data, 0, index_len);
			appendGroup(group, (char*) &file_data, index_len);
			rtn = 1;
		} else {
			id = createMemory(filename.c_str(), HEAD_LENGTH);
			GROUP_HEAD_STRUCT* group_item = readGroupHead(id).getGroupHead();
			FILE_ID_STRUCT file;
			file.size = size;
			file.head = id;
			file.path = str_path;
			file.top = group_item->tag.top;
			file.max = group_item->tag.max;
			file.min = group_item->tag.min;
			filesGroup[group] = file;
			lprint.printFile("load file: %s \n", filename.c_str());
			rtn = 2;
		}
	}

	return rtn;
}

int hasIndexCollect(int id, FILE_LEN_TYPE find, FILE_LEN_TYPE start) {
	lprint.printDebug("function: hasIndexCollect \n");
	MEM_FILE_ITEM mem = findMemoryFile(id);
	if (!mem.isNull()){
		char *mem_char = mem.readMapper().getChar();
		IndexCollectOptStruct *indexCollect =
				(IndexCollectOptStruct*) &mem_char[start];
		if (1 == indexCollect->status && find == indexCollect->key_val) {
			return 1;
		}
	}
	return 0;
}

char* readNext(int id, FILE_LEN_TYPE &start) {
	lprint.printDebug("function: readNext \n");
	MEM_FILE_ITEM mem = findMemoryFile(id);
	if (mem.isNull()){
		return NULL;
	}
	char *mem_char = mem.readMapper().getChar();
	IndexCollectOptStruct *indexCollect =
			(IndexCollectOptStruct*) &mem_char[start];
	char *dest = MEM_CPY_CHARS(*indexCollect);
	return dest;
}

char* readGroup(const char *group_name, FILE_LEN_TYPE pos, FILE_LEN_TYPE size) {
	lprint.printDebug("function: readGroup \n");
	STRING group(group_name);
	createGroupFile(group, pos);
	char *value = readValue(group, pos).getChar();
	char *dest = CHARS(size);
	memcpy(dest, value, size);
	return dest;
}

CHAR_MEM readAndCreate(STRING group_name, FILE_LEN_TYPE pos) {
	lprint.printDebug("function: readAndCreate \n");
	FILE_ID_STRUCT *file_struct = &filesGroup[group_name];
	int id = createGroupFile(group_name, pos);
	MEM_FILE_ITEM mem = findMemoryFile(id);
	if (mem.isNull()){
		return mem.createChar(NULL);
	}
	char *mem_char = mem.readMapper().getChar();
	FILE_LEN_TYPE file_pos = FILE_POS(pos, file_struct->size);
	return mem.createChar(&mem_char[file_pos]);
}

CHAR_MEM readValue(STRING group_name, FILE_LEN_TYPE pos) {
	lprint.printDebug("function: readValue \n");
	FILE_ID_STRUCT *file_struct = &filesGroup[group_name];
	MEM_FILE_ITEM mem = readMemoryMap(group_name, pos);
	if (mem.isNull()){
		return mem.createChar(NULL);
	}
	char *mem_char = mem.readMapper().getChar();
	FILE_LEN_TYPE file_pos = FILE_POS(pos, file_struct->size);
	return mem.createChar(&mem_char[file_pos]);
}

char* groupHeadTag(const char* group_name){
	STRING group(group_name);
	GROUP_HEAD_STRUCT* group_item = readGroupHead(group_name).getGroupHead();
	char *dest = MEM_CPY_CHARS(group_item->tag);
	return dest;
}

GROUP_HEAD_VALUE readGroupHead(STRING group_name) {
	lprint.printDebug("function: readGroupHead group name \n");
	FILE_ID_STRUCT *file_struct = &filesGroup[group_name];
	return readGroupHead(file_struct->head);
}

GROUP_HEAD_VALUE readGroupHead(int id){
	lprint.printDebug("function: readGroupHead id \n");
	MEM_FILE_ITEM mem = findMemoryFile(id);
	char* head_val = mem.isNull() ? NULL : mem.readMapper().getChar();
	GROUP_HEAD_VALUE group_head(head_val, mem.get());
	return group_head;
}

char* groupAppend(const char *group_name, char *data, FILE_LEN_TYPE size) {
	lprint.printDebug("function: groupAppend \n");
	STRING group(group_name);
	APPEND_ITEM item = appendGroup(group, data, size);
	char *dest = MEM_CPY_CHARS(item);
	return dest;
}

void writeGroup(const char *group_name, char *data, FILE_LEN_TYPE pos,
FILE_LEN_TYPE size) {
	lprint.printDebug("function: writeGroup \n");
	STRING group(group_name);
	GROUP_HEAD_STRUCT *head = readGroupHead(group_name).getGroupHead();
	createGroupFile(group_name, head->position);
	FILE_ID_STRUCT *file_struct = &filesGroup[group_name];
	FILE_LEN_TYPE file_pos = FILE_POS(pos, file_struct->size);
	MEM_FILE_ITEM mem = readMemoryMap(group, pos);
	if (!mem.isNull())
		mem.write(data, file_pos, size);
}

APPEND_ITEM appendGroup(STRING group_name, char *data, FILE_LEN_TYPE size) {
	lprint.printDebug("function: appendGroup \n");
	GROUP_HEAD_STRUCT *head = readGroupHead(group_name).getGroupHead();
	int id = createGroupFile(group_name, head->position);
	appendMemory(id, data, size);
	APPEND_ITEM append;
	append.new_pos = head->position + size;
	append.old_pos = head->position;
	head->position = append.new_pos;
	return append;
}

APPEND_ITEM appendGroup(STRING group_name, char *data, FILE_LEN_TYPE size, FILE_RET_TYPE value) {
	lprint.printDebug("function: appendGroup \n");
	GROUP_HEAD_STRUCT *head = readGroupHead(group_name).getGroupHead();
	int id = createGroupFile(group_name, head->position);
	appendMemory(id, data, size, value);
	APPEND_ITEM append;
	append.new_pos = head->position + size;
	append.old_pos = head->position;
	head->position = append.new_pos;
	return append;
}

char* appendSplit(const char *group_name, char *data, int index_len,
FILE_LEN_TYPE size) {
	lprint.printDebug("function: appendSplit \n");
	STRING group(group_name);
	FILE_LEN_TYPE pos = 0;
	APPEND_ITEM pos_item;
	pos_item.old_pos = groupTell(group_name);
	while (pos < size) {
		APPEND_ITEM append_item = appendGroup(group, &data[pos], index_len);
		pos_item.new_pos = append_item.new_pos;
		pos += index_len;
	}

	char *dest = MEM_CPY_CHARS(pos_item);
	return dest;
}

char* findWriteGroup(const char *group_name, char *data) {
	lprint.printDebug("function: findWriteGroup \n");
	STRING group(group_name);
	APPEND_ITEM append_item = findAndWriteGroup(group, data);
	char *dest = MEM_CPY_CHARS(append_item);
	return dest;
}

APPEND_ITEM findAndWriteGroup(STRING group, char *data) {
	lprint.printDebug("function: findAndWriteGroup \n");
	int size = sizeof(IndexCollectOptStruct);
	GROUP_HEAD_STRUCT *head = readGroupHead(group).getGroupHead();
	FILE_LEN_TYPE next_pos = head->tag.top;
	FILE_LEN_TYPE front = FRIST_POS, back = FRIST_POS;
	IndexCollectOptStruct *dataItem = (IndexCollectOptStruct*) data;
	IndexCollectOptStruct *indexCollect;
	do {
		indexCollect = (IndexCollectOptStruct*) readValue(group, next_pos).getChar();
		if (indexCollect->key_val >= dataItem->key_val) {
			front = indexCollect->sfront;
			back = next_pos;
			break;
		} else {
			front = next_pos;
		}
		next_pos = indexCollect->sback;
	} while (next_pos > FRIST_POS);
	dataItem->sfront = front;
	dataItem->sback = back;
	dataItem->status = 1;
	APPEND_ITEM append_pos = appendGroup(group, (char*) dataItem, size, dataItem->key_val);
	indexCollect = (IndexCollectOptStruct*) readValue(group, append_pos.old_pos).getChar();
	indexCollect->pos = append_pos.old_pos;

	if (back > LAST_POS) {
		IndexCollectOptStruct *backItem = (IndexCollectOptStruct*) readValue(
				group, back).getChar();
		backItem->sfront = append_pos.old_pos;
	} else {
		head->tag.last = append_pos.old_pos;
	}

	if (front > FRIST_POS) {
		IndexCollectOptStruct *frontItem = (IndexCollectOptStruct*) readValue(
				group, front).getChar();
		frontItem->sback = append_pos.old_pos;
	} else {
		head->tag.top = append_pos.old_pos;
	}

	head->tag.max = head->tag.max > dataItem->key_val ? head->tag.max : dataItem->key_val;
	head->tag.min = head->tag.min < dataItem->key_val ? head->tag.min : dataItem->key_val;
	head->tag.count++;

	FILE_ID_STRUCT* group_item = findFile(group);
	group_item->top = head->tag.top;
	group_item->max = head->tag.max;
	group_item->min = head->tag.min;
	return append_pos;
}

char* findValueIndex(const char *group_name, FILE_LEN_TYPE start,
FILE_LEN_TYPE i_end, unsigned long long fieldName, int vale_len) {
	lprint.printDebug("function: findValueIndex \n");
	STRING group(group_name);
	ValueIndexStruct *valueIndex = findValueIndexKeys(group_name, start, i_end,
			fieldName, vale_len);
	int size = sizeof(ValueIndexStruct);
	char *dest = CHARS(size);
	if (NULL != valueIndex) {
		memcpy(dest, valueIndex, size);
	}
	return dest;
}

ValueIndexStruct* findValueIndexKeys(STRING group_name, FILE_LEN_TYPE start,
FILE_LEN_TYPE i_end, FILE_LEN_TYPE fieldName, int vale_len) {
	lprint.printDebug("function: findValueIndexKeys \n");
	FILE_LEN_TYPE next_pos = start;
	char status = 1;
	int valueIndexSize = sizeof(ValueIndexStruct);
	while (status > 0 && next_pos < i_end) {
		char *key_val_bytes = readValue(group_name, next_pos).getChar();
		ValueIndexStruct *valueIndex = (ValueIndexStruct*) key_val_bytes;
		next_pos += valueIndexSize * vale_len;
		if (valueIndex->name == fieldName) {
			return valueIndex;
		}
	}
	return NULL;
}

FILE_LEN_TYPE groupTell(const char *group_name) {
	lprint.printDebug("function: groupTell \n");
	STRING group(group_name);
	GROUP_HEAD_STRUCT *head = readGroupHead(group_name).getGroupHead();
	if (NULL == head) {
		return 0;
	}
	return head->position;
}

void groupSeek(const char *group_name, FILE_LEN_TYPE pos){
	lprint.printDebug("function: groupSeek \n");
	STRING group(group_name);
	GROUP_HEAD_STRUCT *head = readGroupHead(group_name).getGroupHead();
	if (NULL != head) {
		head->position = pos;
	}
}

void closeGroup(STRING group_name) {
	lprint.printDebug("function: closeGroup \n");
	FILE_ID_STRUCT *file_struct = findFile(group_name);
	for (auto &file_item : file_struct->fileIds) {
		closeMemory(file_item.second);
	}
}

FILE_RET_TYPE appendIndexDataValue(const char *group_name, FILE_LEN_TYPE field_key,
		char *data, FILE_LEN_TYPE pos, int base_index_len) {
	lprint.printDebug("function: appendIndexDataValue \n");
	STRING group(group_name);
	INDEX_VALUE_DATA *index_data = (INDEX_VALUE_DATA*) data;
	int index_size = sizeof(INDEX_VALUE_DATA);
	FILE_LEN_TYPE key_num = field_key % base_index_len * index_size + pos;
	FILE_LEN_TYPE end = 0;
	INDEX_VALUE_DATA *find = NULL;

	do {
		end = key_num;
		CHAR_MEM char_item = readAndCreate(group, key_num);
		find = (INDEX_VALUE_DATA*) char_item.getChar();
		key_num += base_index_len * index_size;
	} while (0 == find->status);

	if (NULL != find) {
		find->status = 1;
		find->name = index_data->name;
		find->start = index_data->start;
		find->end = index_data->end;
	}

	return end;
}

char* findCollectNextValue(const char* group_name,  FILE_LEN_TYPE front, FILE_LEN_TYPE back, FILE_LEN_TYPE field_key, int type){
	STRING group(group_name);
	IndexCollectOptStruct* collect = findCollectNext(group, front, back, field_key, type);
	if (NULL == collect){
		return NULL;
	}
	char* dest = MEM_CRT_CHARS(collect, sizeof(IndexCollectOptStruct));
	return dest;
}

IndexCollectOptStruct* findCollectNext(STRING group, FILE_LEN_TYPE front, FILE_LEN_TYPE back, FILE_LEN_TYPE field_key, int type){
	FILE_LEN_TYPE pos = lt == (type & lt) ? back : front;
	if (0 == pos){
		GROUP_HEAD_STRUCT *head = readGroupHead(group).getGroupHead();
		pos = lt == (type & lt) ? head->tag.top : head->tag.last;
	}
	IndexCollectOptStruct* collect_item = (IndexCollectOptStruct*) readValue(group, pos).getChar();
//	lprint.printConsole("pos: %llu; collect value: %llu; field value: %llu \n", pos, collect_item->key_val, field_key);
	if (eq == (type & eq) && field_key == collect_item->key_val){
		return collect_item;
	} else if (gt == (type & gt) && collect_item->key_val > field_key){
		return collect_item;
	} else if (lt == (type & lt) && collect_item->key_val < field_key){
		return collect_item;
	}
	return NULL;
}

void close_mem(){
	file_thread.close();
}

void printGroup(const char* group_name){
	STRING group(group_name);
	GROUP_HEAD_STRUCT *head = readGroupHead(group).getGroupHead();
	FILE_LEN_TYPE pos = head->tag.top;
	while (pos > 0){
		IndexCollectOptStruct* collect_item = (IndexCollectOptStruct*) readValue(group, pos).getChar();
		lprint.printConsole("pos: %llu; front: %llu; back: %llu; key value: %llu \n", pos, collect_item->sfront, collect_item->sback, collect_item->key_val);
		pos = collect_item->sback;
	}
}

int deleteGroupData(const char* group_name, FILE_LEN_TYPE pos){
	STRING group(group_name);
	IndexCollectOptStruct *indexCollect = (IndexCollectOptStruct*) readValue(group, pos).getChar();
	if (indexCollect->status > 0){
		if (indexCollect->sback > 0){
			IndexCollectOptStruct *back_item = (IndexCollectOptStruct*) readValue(group, indexCollect->sback).getChar();
			back_item->sfront = indexCollect->sfront;
		}

		if (indexCollect->sfront > 0){
			IndexCollectOptStruct *front_item = (IndexCollectOptStruct*) readValue(group, indexCollect->sfront).getChar();
			front_item->sback = indexCollect->sback;
		}
		indexCollect->status = 0;
		return pos;
	}
	return ERR_NUM;
}

/***************************************************************************************************************************************************/

CREATE_FILE_THREAD::createThread(int count, void* func){
	run_func = func;
	out_cout = count;
	while(count-- > 0){
		THREAD* trd = new THREAD(&createThread::run, this);
		trd_list.push_back(trd);
		trd->detach();
	}
}

void CREATE_FILE_THREAD::add(CREATE_FILE group){
	if (group_names.count(group.group) == 0){
		create_groups.push(group);
		group_names.insert(group.group);
	}
}

void CREATE_FILE_THREAD::end(STRING group_name){
	group_names.erase(group_name);
}

void CREATE_FILE_THREAD::close(){
	run_stat = false;
	while (out_cout > 0){};
	for_each(trd_list.begin(), trd_list.end(), [](THREAD* trd){
		delete trd;
	});
}

void CREATE_FILE_THREAD::run(){
	while(run_stat){
		CREATE_FILE cf = readOne();
		((CREATE_FUNC)run_func)(&cf);
		end(cf.group);
	}
	out_cout--;
}

CREATE_FILE CREATE_FILE_THREAD::readOne(){
	CREATE_FILE cf;
	read_lock.lock();
	do{
		if (!create_groups.empty()){
			cf = create_groups.front();
			create_groups.pop();
			break;
		}
	} while(run_stat);
	read_lock.unlock();
	return cf;
}

/***************************************************************************************************************************************************/
